package org.shiro.serialize;

import org.apache.shiro.codec.Base64;
import org.apache.shiro.codec.CodecSupport;
import org.apache.shiro.codec.Hex;
import org.apache.shiro.util.ByteSource;

import java.io.File;
import java.io.InputStream;
import java.io.Serializable;
import java.util.Arrays;

/**
 * @author 党怡承
 * @version 1.0
 * @date 2021-04-24 8:56
 */
public class ShiroByteSource implements ByteSource, Serializable {
  private static final long serialVersionUID = 5175082362119580768L;

  private byte[] bytes;

  private String cacheHex;

  private String cacheBase64;

  public ShiroByteSource() {}

  public ShiroByteSource(byte[] bytes) {
    this.bytes = bytes;
  }

  public ShiroByteSource(char[] chars) {
    this.bytes = CodecSupport.toBytes(chars);
  }

  public ShiroByteSource(String string) {
    this.bytes = CodecSupport.toBytes(string);
  }

  public ShiroByteSource(ByteSource source) {
    this.bytes = source.getBytes();
  }

  public ShiroByteSource(File file) {
    this.bytes = (new ShiroByteSource.BytesHelper()).getBytes(file);
  }

  public ShiroByteSource(InputStream stream) {
    this.bytes = (new ShiroByteSource.BytesHelper()).getBytes(stream);
  }

  public static boolean isCompatible(Object o) {
    return o instanceof byte[]
        || o instanceof char[]
        || o instanceof String
        || o instanceof ByteSource
        || o instanceof File
        || o instanceof InputStream;
  }

  public void setBytes(byte[] bytes) {
    this.bytes = bytes;
  }

  @Override
  public byte[] getBytes() {
    return this.bytes;
  }

  @Override
  public String toHex() {
    if (this.cacheHex == null) {
      this.cacheHex = Hex.encodeToString(this.getBytes());
    }
    return this.cacheHex;
  }

  @Override
  public String toBase64() {
    if (this.cacheBase64 == null) {
      this.cacheBase64 = Base64.encodeToString(this.getBytes());
    }

    return this.cacheBase64;
  }

  @Override
  public boolean isEmpty() {
    return this.bytes == null || this.bytes.length == 0;
  }

  @Override
  public String toString() {
    return this.toBase64();
  }

  @Override
  public int hashCode() {
    return this.bytes != null && this.bytes.length != 0 ? Arrays.hashCode(this.bytes) : 0;
  }

  @Override
  public boolean equals(Object o) {
    if (o == this) {
      return true;
    } else if (o instanceof ByteSource) {
      ByteSource bs = (ByteSource) o;
      return Arrays.equals(this.getBytes(), bs.getBytes());
    } else {
      return false;
    }
  }

  private static final class BytesHelper extends CodecSupport {
    private BytesHelper() {}

    public byte[] getBytes(File file) {
      return this.toBytes(file);
    }

    public byte[] getBytes(InputStream stream) {
      return this.toBytes(stream);
    }
  }
}
